package com.hksj.Intelligentmanagement.mqtt.listener;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hksj.Intelligentmanagement.entity.CallTaskOrderEntity;
import com.hksj.Intelligentmanagement.entity.CallUserEntity;
import com.hksj.Intelligentmanagement.enums.QosEnum;
import com.hksj.Intelligentmanagement.mqtt.annotation.MqttSubscription;
import com.hksj.Intelligentmanagement.mqtt.command.*;
import com.hksj.Intelligentmanagement.mqtt.constant.Constant;
import com.hksj.Intelligentmanagement.mqtt.constant.TopicConstant;
import com.hksj.Intelligentmanagement.service.CallTaskOrderService;
import com.hksj.Intelligentmanagement.service.CallUserService;
import com.hksj.Intelligentmanagement.utils.RedisCache;
import com.hksj.Intelligentmanagement.utils.UnicodeUtils;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@MqttSubscription(topics = {TopicConstant.WATCH_DATA_RECEIVE})
public class WatchDataListener extends ListenerMqttClientSupport implements MqttListener{
    @Autowired
    private RedisCache<Set<WatchInfoDTO>> watchInfoCache;

    @Autowired
    private RedisCache<Set<String>> reLoginWatchAddrCache;

    @Autowired
    private CallUserService userService;

    @Autowired
    private Redisson redisson;

    @Autowired
    private CallTaskOrderService taskOrderService;

    @Autowired
    private RedisCache<String> strCache;

    private static final String commandStr = "A00604";

    private static final String reLogin_commandStr = "A00605";


    @Override
    public void receive(String topic, MqttMessage mqttMessage) {
        getMqttClient().getCallBackAdapter().getExecutor().execute(() -> {
            JSONObject data = JSONObject.parseObject(new String(mqttMessage.getPayload()));
            String pktType = data.getString("pkt_type");
            if (!StrUtil.isEmpty(pktType)){
                switch (pktType){
                    case "scan_report":
                        initConn(data);
                        break;
                    case "response":
                        String respType = data.getJSONObject("data").getString("resp");
                        if (Objects.equals(respType,CommandConstant.COMMAND_CONN_STATE)){
                            Set<String> reLoginSet = reLoginWatchAddrCache.getCacheObject(CommandConstant.CACHE_RE_LOGIN_WATCH_INFO);
                            JSONArray jsonArray = data.getJSONObject("data").getJSONArray("devices_info");
                            if (!ObjectUtils.isEmpty(jsonArray)){
                                List<WatchInfoDTO> watchInfos = jsonArray.toJavaList(WatchInfoDTO.class);
                                HashSet<WatchInfoDTO> set = new HashSet<>();
                                for (WatchInfoDTO watchInfo : watchInfos) {
                                    if (Objects.equals(watchInfo.getState(),"ready")){
                                        if (ObjectUtils.isEmpty(reLoginSet) || !reLoginSet.contains(watchInfo.getAddr())){
                                            set.add(watchInfo);
                                        }
                                    }
                                }
                                watchInfoCache.setCacheObject(CommandConstant.CACHE_LOGIN_WATCH_INFO,set);
                            }
                        }else{
//                            System.out.println(data.toJSONString());
                        }
                        break;
                    case "state":
                        JSONObject currentData = data.getJSONObject("data");
                        if (!ObjectUtils.isEmpty(currentData)){
                            //抢单
                            JSONObject recvInfos = currentData.getJSONObject("recv_infos");
                            if (!ObjectUtils.isEmpty(recvInfos)){
                                String orderNo = recvInfos.getString("raw").toUpperCase();
                                if (orderNo.startsWith(commandStr)){
                                    String addr = currentData.getString("device_addr");
                                    orderNo = UnicodeUtils.decode(orderNo.substring(commandStr.length()));
                                    //抢单
                                    obtainOrder(addr,orderNo);
                                }

                                //重新登录
                                if(orderNo.startsWith(reLogin_commandStr)){
                                    String deviceAddr = currentData.getString("device_addr");
                                    Msg<DisConnMsgData> msg = new Msg<>();
                                    DisConnMsgData disConnMsgData = new DisConnMsgData();
                                    ArrayList<ConnMsgData.DeviceInfo> deviceInfos = new ArrayList<>();
                                    deviceInfos.add(disConnMsgData.createDeviceInfo(deviceAddr));
                                    disConnMsgData.setDevices_num(deviceInfos.size());
                                    disConnMsgData.setDevices(deviceInfos);
                                    msg.setData(disConnMsgData);


                                    try {
                                        Thread.sleep(1000 * 5);
                                    }catch (Exception e){
                                    }finally {
                                        getMqttClient().publish(JSONObject.toJSONString(msg),CommandConstant.TOPIC_SEND_DATA,QosEnum.Qos2,true);
                                    }
                                }
                            }
                        }
                        break;
                    default:
                        break;
                }
            }else{
                System.out.println(data.toJSONString());
            }
        });
    }

    /**
     * 连接设备
     * @param data
     */
    private void initConn(JSONObject data){
        JSONArray devInfos = data.getJSONObject("data").getJSONArray("dev_infos");
        List<WatchInfoDTO> watchInfos = devInfos.toJavaList(WatchInfoDTO.class);

        Msg<ConnMsgData> msg = new Msg<>();
        ConnMsgData connMsgData = new ConnMsgData();
        ArrayList<ConnMsgData.DeviceInfo> deviceInfos = new ArrayList<>();
        HashSet<String> reLoginSet = new HashSet<>();
        HashSet<WatchInfoDTO> loginedSet = new HashSet<>();
        for (WatchInfoDTO watchInfo : watchInfos) {
            String advRaw = watchInfo.getAdv_raw();
            String loginPasswordCodeHex = advRaw.substring(advRaw.length() - 8,advRaw.length());
            if (!Objects.equals(CommandConstant.EMPTY_PASSWORD_CODE,loginPasswordCodeHex)){
                Object initializingFlag = strCache.getCacheObject(Constant.TO_RELOGINING + watchInfo.getAddr());
                if (!ObjectUtils.isEmpty(initializingFlag)){
                    continue;
                }else{
                    strCache.setCacheObject(Constant.TO_RELOGINING + watchInfo.getAddr(),true,3,TimeUnit.SECONDS);
                }

                String loginPasswordCode = new String(HexUtil.decodeHexStr(loginPasswordCodeHex));
                CallUserEntity user = userService.getUserByDevicePassword(loginPasswordCode);
                if (ObjectUtils.isEmpty(user)){
                    reLoginSet.add(watchInfo.getAddr());
                }else{
                    //挤掉之前登录的
                    if (!ObjectUtils.isEmpty(user.getMac()) && !Objects.equals(watchInfo.getAddr(),user.getMac())){
                        reLoginSet.add(user.getMac());
                    }

                    watchInfo.setName(user.getName());
                    loginedSet.add(watchInfo);
                    //更新用户关联的设备
                    userService.lambdaUpdate().eq(CallUserEntity::getMac,watchInfo.getAddr()).set(CallUserEntity::getMac,null).update();

                    user.setMac(watchInfo.getAddr());
                    userService.updateById(user);
                }
                deviceInfos.add(connMsgData.createDeviceInfo(watchInfo.getAddr()));
            }
        }
        connMsgData.setDevices_num(deviceInfos.size());
        connMsgData.setDevices(deviceInfos);
        msg.setData(connMsgData);

        //更新需要重新登录的手环缓存
        Set<String> cacheSet = reLoginWatchAddrCache.getCacheObject(CommandConstant.CACHE_RE_LOGIN_WATCH_INFO);
        if (ObjectUtils.isEmpty(cacheSet)){
            cacheSet = new HashSet<>();
        }
        cacheSet.removeAll(loginedSet.stream().map(item-> item.getAddr()).collect(Collectors.toSet()));
        cacheSet.addAll(reLoginSet);
        reLoginWatchAddrCache.setCacheObject(CommandConstant.CACHE_RE_LOGIN_WATCH_INFO,cacheSet);

        //连接手环
        if (!ObjectUtils.isEmpty(deviceInfos)){
            getMqttClient().publish(JSONObject.toJSONString(msg),CommandConstant.TOPIC_SEND_DATA,QosEnum.Qos2,true);
        }

        for (String addr : reLoginSet) {
            setUserName(addr, "重新登录");
        }

        //连接成功设置用户信息
        for (WatchInfoDTO watchInfoDTO : loginedSet) {
            setUserName(watchInfoDTO.getAddr(), watchInfoDTO.getName());
        }
    }

    private void setUserName(String addr,String name){
        Msg<SendMsgData> msg = new Msg<>();
        SendMsgData sendMsgData = new SendMsgData();
        ArrayList<SendMsgData.DeviceInfo> deviceInfos = new ArrayList<>();
        deviceInfos.add(sendMsgData.createDeviceInfo(addr));
        sendMsgData.setDevices_num(deviceInfos.size());
        sendMsgData.setDevices(deviceInfos);
        String len = HexUtil.toHex(name.length() * 2);
        len = len.length() == 1 ? "0" + len : len;
        String raw = UnicodeUtils.toUnicode(name);
        StringBuffer sb = new StringBuffer();
        sb.append("A0040500").append("0A").append("020020").append(len.toUpperCase()).append(raw).append("020020");
        sendMsgData.setSend_infos(Arrays.asList(sendMsgData.createSendInfo(sb.toString())));
        msg.setData(sendMsgData);
        //发布任务
        getMqttClient().publish(JSONObject.toJSONString(msg),CommandConstant.TOPIC_SEND_DATA, QosEnum.Qos2, true);
    }


    public void obtainOrder(String addr, String orderNo) {
        //一次校验任务单是否存在
        String flag = strCache.getCacheObject(Constant.TO_OBTAIN_ORDER + orderNo);
        if (StrUtil.isNotBlank(flag)){
            //任务单存在就开始抢单
            RLock rLock = redisson.getLock(Constant.LOCK_OBTAIN_ORDER + orderNo);;
            try{
                //加锁
                if (rLock.tryLock(10,10, TimeUnit.SECONDS)){
                    //二次校验
                    flag = strCache.getCacheObject(Constant.TO_OBTAIN_ORDER + orderNo);

                    if (StrUtil.isNotBlank(flag)){
                        //获取用户信息
                        CallUserEntity user = userService.getUserByMac(addr);
                        if (ObjectUtils.isEmpty(user)){
                            return;
                        }
                        //更新订单信息
                        CallTaskOrderEntity taskOrder = new CallTaskOrderEntity();
                        Date now = new Date();
                        taskOrder.setObtainAt(now);
                        taskOrder.setUpdateAt(now);
                        taskOrder.setState(CallTaskOrderEntity.STATUS_OBTAINED);
                        taskOrder.setObtainDeviceCode(addr);
                        taskOrder.setObtainUserDevicePassword(user.getDevicePassword());
                        taskOrder.setObtainUserId(user.getId());
                        taskOrder.setObtainUsername(user.getName());

                        LambdaQueryWrapper<CallTaskOrderEntity> query = new QueryWrapper<CallTaskOrderEntity>().lambda();
                        query.eq(CallTaskOrderEntity::getOrderNo,orderNo);
                        taskOrderService.update(taskOrder,query);

                        //发送通知
                        sendObtainResult(addr,orderNo);

                        //发送更新首页数据的信息
//                        sendUpdateIndexDataMsg(addr,orderNo,user.getDevicePassword());

                        //获取成功
                        strCache.deleteObject(Constant.TO_OBTAIN_ORDER + orderNo);

                        //删除全局任务待抢标识符
                        CallTaskOrderEntity one = taskOrderService.lambdaQuery()
                                .eq(CallTaskOrderEntity::getOrderNo, orderNo)
                                .last("limit 1")
                                .one();
                        if (one.getType() == 1){
                            strCache.deleteObject(CommandConstant.TASK_ORDER_NEED_TO_OBTAIN_FLAG_1);
                        }else if (one.getType() == 2){
                            strCache.deleteObject(CommandConstant.TASK_ORDER_NEED_TO_OBTAIN_FLAG_2);
                        }
                    }
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                rLock.unlock();
            }
        }else{
            sendObtainFailResultForOne(addr,orderNo);
        }
    }

    private void sendUpdateIndexDataMsg(String addr, String orderNo,String devicePassword) {
        Integer count = taskOrderService.lambdaQuery()
                .eq(CallTaskOrderEntity::getObtainUserDevicePassword, devicePassword)
                .last("and date_format(create_at,'%Y-%m-%d') = " + DateUtil.formatDate(new Date()))
                .count();

        CallTaskOrderEntity taskOrder = taskOrderService.lambdaQuery()
                .eq(CallTaskOrderEntity::getObtainUserDevicePassword, devicePassword)
                .orderByDesc(CallTaskOrderEntity::getCreateAt)
                .last("limit 1")
                .one();

        String msg = taskOrder.getAddress();

        //发布抢单成功通知
        getMqttClient().publish(JSONObject.toJSONString(msg),CommandConstant.TOPIC_SEND_DATA, QosEnum.Qos2, true);
    }

    private void sendObtainResult(String addr,String orderNo){
        sendObtainSuccessResult(addr,orderNo);
        sendObtainFailResult(addr,orderNo);
    }

    private void sendObtainSuccessResult(String addr,String orderNo){
        //发送通知
        Msg<SendMsgData> msg = new Msg<>();

        SendMsgData sendMsgData = new SendMsgData();
        ArrayList<SendMsgData.DeviceInfo> deviceInfos = new ArrayList<>();
        deviceInfos.add(sendMsgData.createDeviceInfo(addr));
        sendMsgData.setDevices_num(deviceInfos.size());
        sendMsgData.setDevices(deviceInfos);
        sendMsgData.setSend_infos(Arrays.asList(sendMsgData.createSendInfo("A0022E01")));

        msg.setData(sendMsgData);

        //发布抢单成功通知
        getMqttClient().publish(JSONObject.toJSONString(msg),CommandConstant.TOPIC_SEND_DATA, QosEnum.Qos2, true);
    }

    private void sendObtainFailResult(String addr,String orderNo){
        Set<WatchInfoDTO> watchInfos = watchInfoCache.getCacheObject(CommandConstant.CACHE_LOGIN_WATCH_INFO);
        if (!ObjectUtils.isEmpty(watchInfos)){
            Msg<SendMsgData> msg = new Msg<>();
            SendMsgData sendMsgData = new SendMsgData();

            ArrayList<SendMsgData.DeviceInfo> deviceInfos = new ArrayList<>();
            for (WatchInfoDTO watchInfo : watchInfos) {
                if (!Objects.equals(addr,watchInfo.getAddr())){
                    deviceInfos.add(sendMsgData.createDeviceInfo(watchInfo.getAddr()));
                }
            }
            sendMsgData.setDevices_num(deviceInfos.size());
            sendMsgData.setDevices(deviceInfos);
            sendMsgData.setSend_infos(Arrays.asList(sendMsgData.createSendInfo("A0022E00")));
            msg.setData(sendMsgData);

            //发布抢单成功通知
            getMqttClient().publish(JSONObject.toJSONString(msg),CommandConstant.TOPIC_SEND_DATA,QosEnum.Qos2,true);
        }
    }

    /**
     * 单个抢单失败响应
     * @param addr
     * @param orderNo
     */
    private void sendObtainFailResultForOne(String addr,String orderNo){
        Msg<SendMsgData> msg = new Msg<>();
        SendMsgData sendMsgData = new SendMsgData();
        ArrayList<SendMsgData.DeviceInfo> deviceInfos = new ArrayList<>();
        deviceInfos.add(sendMsgData.createDeviceInfo(addr));
        sendMsgData.setDevices_num(1);
        sendMsgData.setDevices(deviceInfos);
        sendMsgData.setSend_infos(Arrays.asList(sendMsgData.createSendInfo("A0022E00")));
        msg.setData(sendMsgData);

        //发布抢单成功通知
        getMqttClient().publish(JSONObject.toJSONString(msg),CommandConstant.TOPIC_SEND_DATA,QosEnum.Qos2,true);
    }
}
